#include "log.h"
#include "thread_pool.h"
#include "ghttp.h"
#include "dial.h"

struct http_speed_task_arg {
    int idx;
    dial_arg_t* dial_arg;
    dial_result_t* dial_result;
};

struct time_bytes {
    struct timeval tm;
    int bytes;
};

int http_get_host(char* uri, char* host)
{
    uint16_t port = 0;
    char* tmp_uri;
    assert(host != NULL);

    tmp_uri = strdup(uri);
    ghttp_request* req = ghttp_request_new();
    ghttp_set_uri(req, tmp_uri);
    http_uri* Uri = ghttp_get_uri(req);
    strcpy(host, Uri->host);
    port = Uri->port;
    free(tmp_uri);
    ghttp_request_destroy(req);
    return port;
}

void http_result_print(http_result_t* result)
{
    char bps_str[32], byte_str[32];

    log_trace("%s", result->label);
    log_trace("code:%d", result->code);
    log_trace("latency:%.2f ms", result->latency);
    byte2str(result->total_bytes, byte_str);
    log_trace("total_bytes:%s", byte_str);
    byte2str(result->valid_bytes, byte_str);
    log_trace("valid_bytes:%s", byte_str);
    log_trace("total_time:%.2f ms", result->total_time);
    log_trace("valid_time:%.2f ms", result->valid_time);
    bps2str(result->rate, bps_str);
    log_trace("rate:%s", bps_str);
    bps2str(result->max_rate, bps_str);
    log_trace("max_rate:%s", bps_str);
    bps2str(result->min_rate, bps_str);
    log_trace("min_rate:%s", bps_str);
}

float cal_statics(struct timeval* now, struct time_bytes* tb, http_arg_t* http_arg, http_result_t* result, int ignore)
{
    struct timeval sub;
    float cur_speed = 0;

    if (!timerisset(&tb->tm)) {
        gettimeofday(&tb->tm, NULL);
        tb->bytes = result->total_bytes;
    }
    timersub(now, &tb->tm, &sub);
    // log_tv("now", now);
    // log_debug("tb:%p", tb);
    // log_tv("tm", &tb->tm);
    // log_debug("sub:%.2f ms", TV2MS(&sub));
    // log_debug("http_arg:%p, http_arg->interval:%d", http_arg, http_arg->interval);
    if (TV2MS(&sub) > http_arg->interval) {
        cur_speed = (float)(result->total_bytes - tb->bytes) * 8 / ((float)http_arg->interval / 1000.0);
        // log_trace("result->bytes - tb->bytes:%d", result->total_bytes - tb->bytes);
        // log_trace("cur_speed:%.2f", cur_speed);
        if (http_arg->callback) {
            http_arg->callback((void*)&cur_speed);
        }
        if (!ignore) {
            result->max_rate = MAX_VAL(cur_speed, result->max_rate);
            result->min_rate = result->min_rate ? MIN_VAL(result->min_rate, cur_speed) : cur_speed;
        }
        gettimeofday(&tb->tm, NULL);
        tb->bytes = result->total_bytes;
    }
    return cur_speed;
}

static void* __do_http_upload_task(void* arg)
{
    struct http_speed_task_arg* upload_task_arg = (struct http_speed_task_arg*)arg;
    dial_arg_t* dial_arg = upload_task_arg->dial_arg;
    dial_result_t* dial_result = upload_task_arg->dial_result;
    http_arg_t* http_arg = &upload_task_arg->dial_arg->in.http_arg;
    http_result_t* http_result = &upload_task_arg->dial_result->out.http_result;
    int idx = upload_task_arg->idx;
    int uri_idx = idx % http_arg->uris_count;
    char* uri = http_arg->uris[uri_idx];

    int fd, ret, code = 0, ignore = 1;
    int ignore_duration = 5 * 1000; //ms
    uint32_t diff_ms;
    char buffer[1460] = { 0 }, tmp_buf[128] = { 0 };
    uint32_t diff_time;
    fd_set wrfds, rdfds;
    struct timeval timeo, s_tm, e_tm, now, first_tm = { 0, 0 };
    ghttp_request* req = NULL;
    ghttp_status status;
    struct time_bytes tb = { 0, 0 };

    log_trace("url:%s", uri);
    ignore_duration = http_arg->ignore_duration ? http_arg->ignore_duration : ignore_duration;

    req = ghttp_request_new();
    ghttp_set_header(req, "User-Agent", "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1)");
    ghttp_set_header(req, "Connection", "keep-alive");
    ghttp_set_header(req, "Accept", "*/*");
    ghttp_set_header(req, "Accept-Encoding", "gzip, deflate");
    ghttp_set_header(req, "Accept-Language", "zh-CN,zh;q=0.8,en-US;q=0.5,en;q=0.3");
    ghttp_set_sync(req, ghttp_async);
    if (http_arg->ipaddr) {
        ghttp_set_peerip(req, http_arg->ipaddr);
    }
    if (http_arg->port != 0) {
        ghttp_set_port(req, http_arg->port);
    }
    if (ghttp_set_uri(req, uri) == -1) {
        dial_result->err = HTTP_DIAL_ERR;
        log_error("http set uri error:%s", ghttp_get_error(req));
        goto out;
    }
    if (ghttp_set_type(req, ghttp_type_post) == -1) {
        log_error("http set type error:%s", ghttp_get_error(req));
        dial_result->err = HTTP_DIAL_ERR;
        goto out;
    }
    ghttp_prepare(req);

    sprintf(tmp_buf, "%u", 0xffffffff); //4G
    ghttp_set_header(req, "Content-Length", tmp_buf);
    sprintf(tmp_buf, "multipart/form-data; boundary=----------------%d%d", get_random(), get_random());
    ghttp_set_header(req, "Content-Type", tmp_buf);

    gettimeofday(&s_tm, NULL);
    status = ghttp_process(req);
    if (status == ghttp_error) {
        log_error("http process error:%s.", ghttp_get_error(req));
        dial_result->err = HTTP_DIAL_ERR;
        goto out;
    }
    fd = ghttp_get_socket(req);
    while (1) {
        timeo.tv_sec = 0;
        timeo.tv_usec = 500;
        if (code == 200) {
            FD_ZERO(&wrfds);
            FD_SET(fd, &wrfds);
        }
        FD_ZERO(&rdfds);
        FD_SET(fd, &rdfds);
        ret = select(fd + 1, &rdfds, &wrfds, NULL, &timeo);
        if (ret < 0) {
            log_syserr("select error.");
            dial_result->err = HTTP_DIAL_ERR_SELECT;
            goto out;
        }
        gettimeofday(&now, NULL);
        diff_ms = TV2MS(&now) - TV2MS(&s_tm);
        if (diff_ms > ignore_duration) {
            ignore = 0;
        }
        // cal_statics(&now, &tb, http_arg, http_result, ignore);

        if (diff_ms > http_arg->duration && http_arg->duration > 0) {
            break;
        }
        if (FD_ISSET(fd, &rdfds)) {
            if (!timerisset(&first_tm)) {
                gettimeofday(&first_tm, NULL);
            }
            status = ghttp_process(req);
            if (status == ghttp_error) {
                log_error("http upload error:%s", ghttp_get_error(req));
                dial_result->err = HTTP_DIAL_ERR;
                break;
            } else {
                code = ghttp_status_code(req);
                if (code != 200) {
                    log_trace("http upload code:%d", code);
                    break;
                }
            }
        }
        if (FD_ISSET(fd, &wrfds)) {
            ret = write(fd, buffer, sizeof(buffer));
            if (ret < 0) {
                log_syserr("http upload write error, fd(%d).", fd);
                dial_result->err = HTTP_DIAL_ERR;
                goto out;
            }
            http_result->total_bytes += ret;
            if (!ignore) {
                http_result->valid_bytes += ret;
                if (http_arg->valid_bytes_ptr) {
                    http_arg->valid_bytes_ptr[idx] = http_result->valid_bytes;
                }
            }
        }
    }
    gettimeofday(&e_tm, NULL);

    diff_time = TV2MS(&e_tm) - TV2MS(&s_tm);
    http_result->total_time = diff_time;
    http_result->valid_time = diff_time - ignore_duration;
    http_result->code = code;
    http_result->latency = TV2MS(&first_tm) - TV2MS(&s_tm);
    if (http_result->valid_time <= 0) {
        http_result->rate = ((float)http_result->total_bytes * 8) / ((float)http_result->total_time / 1000.0);
    } else {
        http_result->rate = ((float)http_result->valid_bytes * 8) / ((float)http_result->valid_time / 1000.0);
    }
    strcpy(http_result->label, "http upload");

out:
    ghttp_request_destroy(req);
    return (void*)dial_result;
}

void* do_http_upload(void* arg)
{
    int i = 0;
    dial_arg_t* dial_arg;
    dial_result_t *dial_result, *dial_result_ptr, *total_result;
    http_arg_t* http_arg;
    struct http_speed_task_arg* upload_task_arg;
    pthread_t thid[32];
    int threads_count = 0;

    dial_arg = (dial_arg_t*)arg;
    http_arg = &dial_arg->in.http_arg;

    if (http_arg->threads == 0) {
        http_arg->threads = http_arg->uris_count;
    }

    threads_count = http_arg->threads > Array_Size(thid) ? Array_Size(thid) : http_arg->threads;

    dial_result = calloc(threads_count, sizeof(dial_result_t));
    total_result = calloc(1, sizeof(dial_result_t));

    upload_task_arg = calloc(threads_count, sizeof(struct http_speed_task_arg));

    if (http_arg->uris == NULL) {
        total_result->err = HTTP_DIAL_ERR;
        return total_result;
    }

    dial_result_ptr = dial_result;
    for (i = 0; i < threads_count; i++) {
        upload_task_arg[i].dial_arg = dial_arg;
        upload_task_arg[i].dial_result = (dial_result_ptr++);
        upload_task_arg[i].idx = i;
        pthread_create(&thid[i], NULL, __do_http_upload_task, (void*)&upload_task_arg[i]);
        // thread_pool_add_task(thread_pool, __do_http_upload_task, (void*)&upload_task_arg[i]);
    }

    for (i = 0; i < threads_count; i++) {
        pthread_join(thid[i], NULL);
    }

    dial_result_ptr = dial_result;
    for (i = 0; i < threads_count; i++) {
        if(dial_result_ptr->err != HTTP_DIAL_SUCCESS){
            total_result->err = dial_result_ptr->err;
        }
        total_result->out.http_result.total_time = http_arg->duration;
        total_result->out.http_result.valid_time = http_arg->duration - http_arg->ignore_duration;
        total_result->out.http_result.total_bytes += dial_result_ptr->out.http_result.total_bytes;
        total_result->out.http_result.valid_bytes += dial_result_ptr->out.http_result.valid_bytes;
        total_result->out.http_result.rate += dial_result_ptr->out.http_result.rate;
        dial_result_ptr++;
    }
    free(upload_task_arg);
    free(dial_result);
    return (void*)total_result;
}

static void* __do_http_download_task(void* arg)
{
    char* buf = NULL;
    int ret, code = 0, ignore = 1;
    int ignore_duration = 5 * 1000; //ms
    ghttp_request* req = NULL;
    ghttp_status status;
    uint32_t diff_time, diff_ms;
    struct timeval first_tm = { 0, 0 }, s_tm, e_tm, timeo, now;
    int fd = 0;
    fd_set rdfds;
    struct time_bytes tb = { 0, 0 };
    char buffer[2048] = {0};

    struct http_speed_task_arg* download_task_arg = (struct http_speed_task_arg*)arg;
    dial_arg_t* dial_arg = download_task_arg->dial_arg;
    dial_result_t* dial_result = download_task_arg->dial_result;
    http_arg_t* http_arg = &download_task_arg->dial_arg->in.http_arg;
    http_result_t* http_result = &download_task_arg->dial_result->out.http_result;
    int idx = download_task_arg->idx;
    int uri_idx = idx % http_arg->uris_count;
    char* uri = http_arg->uris[uri_idx];

    log_trace("url:%s", uri);
    if (http_arg->ignore_duration != 0) {
        ignore_duration = http_arg->ignore_duration;
    }
    req = ghttp_request_new();
    if(!req){
        log_trace("ghttp request new failed.");
        dial_result->err = HTTP_DIAL_ERR;
        goto out;
    }
    ghttp_set_chunksize(req, 1024);
    ghttp_set_header(req, "User-Agent", "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1)");
    ghttp_set_header(req, "Connection", "keep-alive");
    ghttp_set_header(req, "Accept", "*/*");
    ghttp_set_header(req, "Accept-Encoding", "gzip, deflate");
    ghttp_set_header(req, "Accept-Language", "zh-CN,zh;q=0.8,en-US;q=0.5,en;q=0.3");
    ghttp_set_sync(req, ghttp_async);
    if (http_arg->ipaddr) {
        ghttp_set_peerip(req, http_arg->ipaddr);
    }
    if (ghttp_set_uri(req, uri) == -1) {
        log_error("http set uri error:%s", ghttp_get_error(req));
        dial_result->err = HTTP_DIAL_ERR;
        goto out;
    }
    if (ghttp_set_type(req, ghttp_type_get) == -1) {
        log_error("http set type error:%s", ghttp_get_error(req));
        dial_result->err = HTTP_DIAL_ERR;
        goto out;
    }
    if (http_arg->port != 0) {
        ghttp_set_port(req, http_arg->port);
    }
    ghttp_prepare(req);

    gettimeofday(&s_tm, NULL);
    status = ghttp_process(req);
    if (status == ghttp_error) {
        log_error("http process error:%s", ghttp_get_error(req));
        dial_result->err = HTTP_DIAL_ERR;
        goto out;
    }
    status = ghttp_process(req);
    if (status == ghttp_error) {
        log_error("http process error:%s", ghttp_get_error(req));
        dial_result->err = HTTP_DIAL_ERR;
        goto out;
    }
    fd = ghttp_get_socket(req);
    while (1) {
        timeo.tv_sec = 0;
        timeo.tv_usec = 500;
        FD_ZERO(&rdfds);
        FD_SET(fd, &rdfds);
        ret = select(fd + 1, &rdfds, NULL, NULL, &timeo);
        if (ret < 0) {
            log_syserr("select error.");
            dial_result->err = HTTP_DIAL_ERR;
            goto out;
        }
        gettimeofday(&now, NULL);
        diff_ms = TV2MS(&now) - TV2MS(&s_tm);
        if (diff_ms > ignore_duration) {
            ignore = 0;
        }

        // cal_statics(&now, &tb, http_arg, http_result, ignore);

        if (diff_ms > http_arg->duration && http_arg->duration > 0) {
            break;
        }

        if (FD_ISSET(fd, &rdfds)) {
            if (!timerisset(&first_tm)) {
                gettimeofday(&first_tm, NULL);
            }
            // status = ghttp_process(req);
            // if (status == ghttp_error) {
            //     log_trace("http error:%s", ghttp_get_error(req));
            //     dial_result->err = HTTP_DIAL_ERR;
            //     goto out;
            // } else if (status == ghttp_done)
            //     break;
            // http_result->total_bytes += ghttp_get_body_len(req);
            // if(http_arg->rt_bytes_ptr){
            //     http_arg->rt_bytes_ptr[idx] = http_result->total_bytes;
            // }
            // if (!ignore) {
            //     http_result->valid_bytes += ghttp_get_body_len(req);
            //     if(http_arg->valid_bytes_ptr){
            //         http_arg->valid_bytes_ptr[idx] = http_result->valid_bytes;
            //     }
            // }
            // if (ghttp_get_body_len(req) >  10 * 1024){
            //     ghttp_flush_response_buffer(req);
            // }

            gettimeofday(&e_tm, NULL);

            ret = read(fd, buffer, sizeof(buffer));
            if (ret < 0) {
                if (errno == EINTR){
                    break;
                }
                else{
                    dial_result->err = HTTP_DIAL_ERR_READ;
                    goto out;
                }
            }

            http_result->total_bytes += ret;
            if (http_arg->rt_bytes_ptr) {
                http_arg->rt_bytes_ptr[idx] = http_result->total_bytes;
            }
            if (!ignore) {
                http_result->valid_bytes += ret;
                if (http_arg->valid_bytes_ptr) {
                    http_arg->valid_bytes_ptr[idx] = http_result->valid_bytes;
                }
            }
        }
    }

    diff_time = TV2MS(&e_tm) - TV2MS(&s_tm);
    http_result->total_time = diff_time;
    http_result->valid_time = diff_time - ignore_duration;
    if (http_result->valid_time <= 0) {
        http_result->rate = ((float)http_result->total_bytes * 8) / ((float)http_result->total_time / 1000.0);
    } else {
        http_result->rate = ((float)http_result->valid_bytes * 8) / ((float)http_result->valid_time / 1000.0);
    }
    http_result->code = ghttp_status_code(req);
    if(timerisset(&first_tm)){
        http_result->latency = TV2MS(&first_tm) - TV2MS(&s_tm);
    }
    strcpy(http_result->label, "http download");
out:
    if(req)
        ghttp_request_destroy(req);
    return NULL;
}

void* do_http_download(void* arg)
{
    int i = 0;
    dial_arg_t* dial_arg;
    dial_result_t *dial_result, *dial_result_ptr, *total_result;
    http_arg_t* http_arg;
    struct http_speed_task_arg* download_task_arg;
    pthread_t thid[32];
    int threads_count = 0;

    dial_arg = (dial_arg_t*)arg;
    http_arg = &dial_arg->in.http_arg;

    if (http_arg->threads == 0) {
        http_arg->threads = http_arg->uris_count;
    }

    threads_count = http_arg->threads > Array_Size(thid) ? Array_Size(thid) : http_arg->threads;

    dial_result = calloc(threads_count, sizeof(dial_result_t));
    total_result = calloc(1, sizeof(dial_result_t));

    download_task_arg = calloc(threads_count, sizeof(struct http_speed_task_arg));

    if (http_arg->uris == NULL) {
        total_result->err = HTTP_DIAL_ERR;
        free(dial_result);
        return total_result;
    }

    dial_result_ptr = dial_result;
    for (i = 0; i < threads_count; i++) {
        download_task_arg[i].dial_arg = dial_arg;
        download_task_arg[i].dial_result = (dial_result_ptr++);
        download_task_arg[i].idx = i;
        pthread_create(&thid[i], NULL, __do_http_download_task, (void*)&download_task_arg[i]);
    }

    for (i = 0; i < threads_count; i++) {
        pthread_join(thid[i], NULL);
    }

    dial_result_ptr = dial_result;
    for (i = 0; i < threads_count; i++) {
        if(dial_result_ptr->err != HTTP_DIAL_SUCCESS){
            total_result->err = dial_result_ptr->err;
        }
        total_result->out.http_result.latency += dial_result_ptr->out.http_result.latency;
        total_result->out.http_result.total_time = http_arg->duration;
        total_result->out.http_result.valid_time = http_arg->duration - http_arg->ignore_duration;
        total_result->out.http_result.total_bytes += dial_result_ptr->out.http_result.total_bytes;
        total_result->out.http_result.valid_bytes += dial_result_ptr->out.http_result.valid_bytes;
        total_result->out.http_result.rate += dial_result_ptr->out.http_result.rate;
        dial_result_ptr++;
    }
    //平均首包时延
    total_result->out.http_result.latency = total_result->out.http_result.latency / threads_count;

    free(download_task_arg);
    free(dial_result);
    return (void*)total_result;
}

void* do_http(void* arg)
{
    char* buf = NULL;
    int ret, code = 0;
    int ignore_duration = 5 * 1000; //ms
    ghttp_request* req = NULL;
    ghttp_status status;
    struct timeval first_tm = { 0, 0 }, s_tm, e_tm, timeo, now;
    int fd = 0;
    fd_set rdfds;
    struct time_bytes tb = { 0, 0 };

    dial_arg_t* dial_arg = (dial_arg_t*)arg;
    dial_result_t* dial_result = calloc(1, sizeof(dial_result_t));
    http_arg_t* http_arg = &dial_arg->in.http_arg;
    http_result_t* http_result = &dial_result->out.http_result;

    req = ghttp_request_new();
    // ghttp_set_chunksize(req, 10* 1024);
    ghttp_set_header(req, "User-Agent", "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1)");
    ghttp_set_header(req, "Connection", "keep-alive");
    ghttp_set_header(req, "Accept", "*/*");
    ghttp_set_header(req, "Accept-Encoding", "gzip, deflate");
    ghttp_set_header(req, "Accept-Language", "zh-CN,zh;q=0.8,en-US;q=0.5,en;q=0.3");
    ghttp_set_sndtmo(req, 3);
    ghttp_set_sync(req, ghttp_async);
    if (http_arg->ipaddr) {
        ghttp_set_peerip(req, http_arg->ipaddr);
    }
    if (ghttp_set_uri(req, http_arg->uri) == -1) {
        log_error("http set uri error:%s", ghttp_get_error(req));
        dial_result->err = HTTP_DIAL_ERR_URI;
        goto out;
    }
    if (ghttp_set_type(req, ghttp_type_get) == -1) {
        log_error("http set type error:%s", ghttp_get_error(req));
        dial_result->err = HTTP_DIAL_ERR_TYPE;
        goto out;
    }
    if (http_arg->port != 0) {
        ghttp_set_port(req, http_arg->port);
    }
    ghttp_prepare(req);

    status = ghttp_process(req);
    if (status == ghttp_error) {
        dial_result->err = HTTP_DIAL_ERR_CONNECT;
        goto out;
    }
    fd = ghttp_get_socket(req);

    gettimeofday(&s_tm, NULL);
    while (1) {
        timeo.tv_sec = http_arg->timeout ? http_arg->timeout : 3;
        timeo.tv_usec = 0;
        FD_ZERO(&rdfds);
        FD_SET(fd, &rdfds);
        ret = select(fd + 1, &rdfds, NULL, NULL, &timeo);
        if (ret < 0) {
            log_syserr("select error.");
            dial_result->err = HTTP_DIAL_ERR_SELECT;
            goto out;
        } else if (ret == 0) {
            log_trace("http dial %s timeout.", http_arg->uri);
            dial_result->err = HTTP_DIAL_ERR_TIMEOUT;
            goto out;
        }

        if (FD_ISSET(fd, &rdfds)) {
            if (!timerisset(&first_tm)) {
                gettimeofday(&first_tm, NULL);
            }
            status = ghttp_process(req);
            if (status == ghttp_error) {
                log_syserr("http error:%s", ghttp_get_error(req));
                dial_result->err = HTTP_DIAL_ERR_FAILED;
                goto out;
            } else if (status == ghttp_done) {
                log_trace("http dial done.");
                break;
            }
            if(ghttp_status_code(req) != 200){
                log_trace("http response code is :%d", ghttp_status_code(req));
                break;
            }
            
        }
    }
    gettimeofday(&e_tm, NULL);
    // log_trace("body:%s", ghttp_get_body(req));
    http_result->total_bytes = ghttp_get_body_len(req);
    http_result->total_time = TV2MS(&e_tm) - TV2MS(&s_tm);
    http_result->rate = ((float)http_result->total_bytes * 8) / ((float)http_result->total_time / 1000.0);
    http_result->code = ghttp_status_code(req);
    http_result->latency = TV2MS(&first_tm) - TV2MS(&s_tm);
    strncpy(http_result->label, http_arg->uri, sizeof(http_result->label));
out:
    if (dial_result->err < 0) {
        http_result->code = dial_result->err;
    }
    ghttp_request_destroy(req);
    return (void*)dial_result;
}


void* do_http_light(void* arg){
    ghttp_request* req = NULL;

    dial_arg_t* dial_arg = (dial_arg_t*)arg;
    dial_result_t* dial_result = calloc(1, sizeof(dial_result_t));
    http_arg_t* http_arg = &dial_arg->in.http_arg;
    http_result_t* http_result = &dial_result->out.http_result;

    if(http_arg->uri == NULL){
        log_trace("http url is NULL");
        dial_result->err = HTTP_DIAL_ERR_URI;
        goto out;
    }

    req = ghttp_request_new();
    if(!req){
        log_syserr("new ghttp request failed.");
        dial_result->err = HTTP_DIAL_ERR;
        goto out;
    }
    ghttp_set_header(req, "User-Agent", "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1)");
    ghttp_set_header(req, "Connection", "keep-alive");
    ghttp_set_header(req, "Accept", "*/*");
    ghttp_set_header(req, "Accept-Encoding", "gzip, deflate");
    ghttp_set_header(req, "Accept-Language", "zh-CN,zh;q=0.8,en-US;q=0.5,en;q=0.3");
    ghttp_set_type(req, ghttp_type_get);
    ghttp_set_sndtmo(req, http_arg->timeout);
    ghttp_set_rcvtmo(req, http_arg->timeout);
    if( 0 > ghttp_set_uri(req, http_arg->uri)){
        log_trace("ghttp_set_uri error, uri:%s", http_arg->uri);
        dial_result->err = HTTP_DIAL_ERR_URI;
        goto out;
    }
    ghttp_prepare(req);
    if(ghttp_error == ghttp_process(req)){
        log_trace("ghttp process error, %s", ghttp_get_error(req));
        dial_result->err = HTTP_DIAL_ERR_FAILED;
        goto out;
    }

    // log_trace("code:%d", ghttp_status_code(req));
    // log_trace("body len:%d", ghttp_get_body_len(req));
    // log_trace("body:%s" ghttp_get_body(req));
    http_result->total_bytes = ghttp_get_body_len(req);
    if(TV2MS(ghttp_finish_tm(req)) == 0){
        http_result->total_time = TV2MS(ghttp_first_rcv_tm(req)) - TV2MS(ghttp_conn_tm(req));
    }else{
        http_result->total_time = TV2MS(ghttp_finish_tm(req)) - TV2MS(ghttp_conn_tm(req));
    }
    http_result->rate = ((float)http_result->total_bytes * 8) / ((float)http_result->total_time / 1000.0);
    http_result->code = ghttp_status_code(req);
    http_result->latency = TV2MS(ghttp_first_rcv_tm(req)) - TV2MS(ghttp_send_tm(req));
    strncpy(http_result->label, http_arg->uri, sizeof(http_result->label));

out:
    if(req){
        ghttp_request_destroy(req);
    }
    return (void*)dial_result;
}